 
import { FactoryTaskHelper } from "GameLoop/WorldState/EntityManager/Entitys/room/factory/FactoryTaskHelper";
import { market_price_limit } from "utils/constants/definetypes";
import { BaseMinerial, BaseMinerialConst, UnZipMinerial, ZipMinerial } from "utils/constants/globalconstant";
import { REACTION_SOURCE } from "utils/constants/constants";
import { LabTaskHelper } from "utils/task/LabTaskHelper";
import { CoreTaskHelper } from "utils/task/coreTaskHelper";

/*
主要功能。
A.对外接口
1.提供资源卖价的建议。
2.提供资源买价的建议。

B.内部数据
1.资源价格的记录。
2.建立资源的线性回归方程
3.自动收集资源价格,建立参考价
4.每多少Tick更新回归方程
*/
/*
买入价= 平均价 + 距离*能量价格  对于低价资源
*/
interface IPriceHistory extends PriceHistory
{
    time: number;
}

interface IRoomOrders
{
    orders:{[key in ResourceConstant]: IOrder};
}
// interface IMyDeal
// {
//     orderId:string;
//     room:string;
//     amount:number;
// }
export class MarketRecorder
{
    PriceHistory!:Record<MarketResourceConstant,IPriceHistory[]>; // 价格历史记录
    LastUpdateTime!:number;  // 最后更新时间
    // TimeSpawn:number;   // 每次更新的时间间隔
 
    PriceEquation!:Record<MarketResourceConstant,IEquation>;

    // @memory_cache(["orders"])
    roomorders!:Record<string,IRoomOrders>;

    // marketdeals:Record<string,IMyDeal>={};
    constructor()
    {
        this.PriceHistory={} as any;
        this.LastUpdateTime=0;
        if(!this.roomorders)
        {
            this.roomorders={};
        }
        if(!this.PriceEquation)
        {
            this.PriceEquation={} as any;
        }
    }
}

export interface IEquation
{
    a:number;
    b:number;
}

export interface IPriceEquation
{
    Equation:Record<MarketResourceConstant,IEquation>;
}

export class MarketPriceGuidance
{
    MarketData:MarketRecorder;
    TimeSpawn:number = 100000;
    constructor()
    {
        this.MarketData  =  new MarketRecorder();
    //    this.MarketData.PriceEquation =  global.MemAlloc.GetMemory(["MarketData"]);
    }

    // @cpu_used()
    public Update()
    {
        // console.log(`update market price`);
        if(this.IsUpdate())
        {
            this.MarketData.PriceEquation;
            this.MarketData.LastUpdateTime = Game.time;
            console.log(`更新加个${Game.time}   --- ${ this.MarketData.LastUpdateTime }`);
            this.UpdatePriceHistory();
            this.UpdateEquation();

            // this.UpdateOrderPrice();
        }
        if(global.canExecute(1001))
        {
            for(const item in Game.market.orders)
            {
                const order = Game.market.orders[item];
                if(order.remainingAmount == 0)
                {
                    Game.market.cancelOrder(order.id);
                }
                else
                {
                    if(order.roomName)
                    {
                        if(!this.MarketData.roomorders[order.roomName])
                        {
                            this.MarketData.roomorders[order.roomName]={orders:{}} as any;
                        }
                        if(!this.MarketData.roomorders[order.roomName].orders)
                        {
                            this.MarketData.roomorders[order.roomName]={orders:{}} as any;
                        }
                        const restype = order.resourceType as ResourceConstant;
                        this.MarketData.roomorders[order.roomName].orders[ restype ]={
                            id:item,
                            type:order.type as ordertype,
                            resourceType:order.resourceType as ResourceConstant,
                            amount:order.amount
                        }
                    }
    
                }

            }
        }
        
    }

    //
    protected UpdateOrderPrice()
    {
        for(const item in Game.market.orders)
        {
            const order = Game.market.orders[item];
            if(order.remainingAmount > 0 && order.type=="sell")
            {
                const price = this.GetSellPrice(order.resourceType);
                Game.market.changeOrderPrice(order.id,price);
            }

        }
    }
    // 获取买价
    public GetBuyPrice(resource:MarketResourceConstant,distance:number=5):number
    {
        let price  = this.GetBasePrice(resource,Math.min);

        // 限制压缩元素矿的价格
        if(UnZipMinerial[resource as CommodityConstant])
        {
            price = Math.min(price,250);
        } // 限制元素矿得买价
        else if(BaseMinerialConst.has(resource as MineralConstant))
        {
            // 元素矿最多50
            price = Math.min(price,50);
        }
        const cost_fee = this.GetResourceTransactionFee(distance);
        return price+cost_fee;
    }

    private GetBasePrice(resource:MarketResourceConstant,com:(a:number,b:number)=>number=Math.min):number
    {
        if(!Memory.Debug["price"])
        {
            Memory.Debug["price"]={};
        }
        if(REACTION_SOURCE[resource  ])
        {
            const price =  this.GetMinerialComposePrice(resource as MineralCompoundConstant);
            Memory.Debug["price"][resource]=price;
            return price;
        }
        else if (BaseMinerial.has(resource as ResourceConstant))
        {
            let price =  this.GetMarketPrice(resource);
            if(market_price_limit[Game.shard.name] && market_price_limit[Game.shard.name][resource])
            {
                price = com(price,market_price_limit[Game.shard.name][resource]);
            }
            return price;
        }
        else
         if (COMMODITIES[resource as CommodityConstant])
        {
            const price =  this.GetProductComposePrice(resource as CommodityConstant);
            Memory.Debug["price"][resource]=price;
            return price;
        }
        else
        {
            let price =  this.GetMarketPrice(resource);
            Memory.Debug["price"][resource]=price;
            return price;
        }

    }
    /**
     * 获取市场预计售价
    */
    private GetMarketPrice(resource:MarketResourceConstant):number
    {
        const equation = this.MarketData.PriceEquation[resource];
        if(equation)
        {
            return equation.a + equation.b * Game.time;
        }
        if(this.MarketData.PriceHistory[resource])
        {
            const avgprice =  this.MarketData.PriceHistory[resource][0].avgPrice;
            return avgprice;
        }
        else
        {
            const hittory = Game.market.getHistory(resource)[0];
            if(hittory)
            {
                return Game.market.getHistory(resource)[0].avgPrice;
            }

        }
        return  1
    }
    /**
     * ResourceType 获取化合物的原材料价格
    */
    private GetMinerialComposePrice(ResourceType:MineralCompoundConstant)
    {
        const record =  LabTaskHelper.getMinerialComposeRecord(ResourceType);
        let price=0;
        for(const key in record)
        {

            price+=record[key as ResourceConstant]*this.GetMarketPrice(key as MarketResourceConstant);
        }
        return price;
    }
    private GetProductComposePrice(ResourceType:CommodityConstant)
    {
        // if(!COMMODITIES[ResourceType])
        // {
        //     return undefined
        // }
        const record =  FactoryTaskHelper.getProductComposeRecord(ResourceType);
        let price=0;
        for(const key in record)
        {

            price+=record[key as ResourceConstant]*this.GetMarketPrice(key as MarketResourceConstant);
        }
        return price/COMMODITIES[ResourceType].amount;
    }
    // 获取卖价
    public GetSellPrice(resource:MarketResourceConstant):number
    {
        const baseprice  = this.GetBasePrice(resource,Math.max);
        const market_price = this.GetMarketPrice(resource);
        Memory.Debug[`价格计算:${resource}`]=`[${Game.time}]:成本价格[${baseprice}] 市场价格:[${market_price}]`
        const price = Math.max(baseprice*1.1,market_price);
        if(Game.market.credits<1000000)
        {
            return price*0.95;
        }
        return price*1.001;
    }

    public GetOrders(roomName:string)
    {
        return this.MarketData.roomorders[roomName];
    }
    //
    // @cpu_used()
    /**
     * 获取自己的订单
    */
    public GetOrder(roomName:string,resource:ResourceConstant):IOrder|undefined
    {
        if(this.MarketData.roomorders[roomName])
        {
            const order = this.MarketData.roomorders[roomName].orders[resource];
            if(!order)
            {

                console.log(`my orders :${JSON.stringify(Game.market.orders)}`);
                // order 不存在
                const order  = _.find(Game.market.orders,item=>item.roomName == roomName && item.resourceType == resource);
                if(order)
                {
                    this.MarketData.roomorders[roomName].orders[resource] = {id:order.id,type:order.type as ordertype,resourceType:order.resourceType as ResourceConstant,amount:order.remainingAmount};
                    return this.MarketData.roomorders[roomName].orders[resource];
                }
            }
            else
            {
                if(order.id)
                {
                   const marketorder =  Game.market.getOrderById(order.id);
                   if(!marketorder  ||  marketorder?.remainingAmount ==0)
                   {
                        Game.market.cancelOrder(order.id);
                          delete this.MarketData.roomorders[roomName].orders[resource];
                          return undefined;
                   }
                   else
                   {
                          return order;
                   }
                }
            }
        }
        return undefined;
    }

    //
    // @cpu_used()
    public CreateOrder(roomName:string,order:IOrder,m:number=1):boolean
    {
        if(this.GetOrder(roomName,order.resourceType))
        {
            console.log(`已经存在订单 ${roomName}:[ ${order.resourceType}]`);
            return false;
        }
        if(!this.MarketData.roomorders[roomName])
        {
            this.MarketData.roomorders[roomName] = { orders:{} as any};
        }
        let price=0;

        if(order.type == "sell")
        {
            price = this.GetSellPrice(order.resourceType)*m;

        }
        else
        {
            price = this.GetBuyPrice(order.resourceType)*m;
            // 买单默认算10格内得路费

        }

        const orderparam={
            type:order.type ,
            resourceType: order.resourceType as MarketResourceConstant,
            price: price,
            totalAmount: order.amount,
            roomName:roomName,
        }
        console.log(`market 创建order ${JSON.stringify(orderparam)}`);
        const code = Game.market.createOrder(orderparam);
        if(code === OK)
        {
            return true;
        }
        return false;
    }
    /**
     *
     * @param roomName
     * @param ResourceType
     * @param amount
     * @param m 最终得价格乘以系数,默认是1
     * @returns
     */
    public PostBuyOrder(roomName:string,ResourceType:MarketResourceConstant,amount:number,m:number=1)
    {

        // const resourcePrice = this.GetBuyPrice(ResourceType);
        return this.CreateOrder(roomName,{
            type:"buy",
            resourceType:ResourceType as ResourceConstant,
            amount:amount
        },m)

    }
   /**
     *
     * @param roomName
     * @param ResourceType
     * @param amount
     * @param m 最终得价格乘以系数,默认是1
     * @returns
     */
   public PostSellOrder(roomName:string,ResourceType:MarketResourceConstant,amount:number,m:number=1)
   {

       // const resourcePrice = this.GetBuyPrice(ResourceType);
       return this.CreateOrder(roomName,{
           type:"sell",
           resourceType:ResourceType as ResourceConstant,
           amount:amount
       },m)

   }
    /**
     * 购买资源
    */
    public BuyResource(roomName:string,ResourceType:MarketResourceConstant,amount:number,default_range:number=10,m:number=1)
    {


        let range = ResourceType== RESOURCE_ENERGY?10:default_range;
        let buyprice = this.GetBuyPrice(ResourceType,range)*m;
        const orders =Game.market.getAllOrders({type: ORDER_SELL, resourceType: ResourceType});
        let leftamnount = amount;

        for(const order of orders)
        {
            if(!order.roomName)
            {
                continue;
            }

            {
                if(order.price>buyprice)
                {
                    continue;
                }
                const fee = this.CalResourceTransactionFee(roomName,order.roomName,ResourceType);

                // 这里的价格可能需要处理一下,或者是房间在距离范围10格之内
                if((order.price+fee)<buyprice )
                {
                    // 消费+价格小于 平均价格就购买
                    const buycount = Math.min(leftamnount,order.amount);
                    leftamnount-=buycount;
                    const task = CoreTaskHelper.getDeal(order.id,"buy",buycount);
                    Game.rooms[roomName].addNewTask(task)
                    // Game.market.deal(order.id,buycount,roomName);
                    if(leftamnount<=0)
                    {
                        return true;
                    }
                }
            }

            // global.LogDebug(`${roomName}_${ResourceType}_购买价格:${resourcePrice} 路费:${fee}  订单价格:${order.price}`);
        }
        return false;
    }

    /**
     * 出售资源
    */
     public SellResource(roomName:string,ResourceType:ResourceConstant,amount:number)
     {

         const resourcePrice = this.GetSellPrice(ResourceType);
         // global.LogDebug(`出售资源:${roomName}_${ResourceType}_${amount} 限定卖价:${resourcePrice}`);
         const orders =Game.market.getAllOrders({type: ORDER_BUY, resourceType: ResourceType});
         let leftamnount = amount;
         for(const order of orders)
         {
             //global.LogDebug(`出售资源: ${roomName}_${ResourceType}_购买价格:${resourcePrice}   订单价格:${order.price}`);

             if(!order.roomName)
             {
                 continue;
             }
             if(order.price<resourcePrice)
             {
                 continue;
             }
             if(ResourceType == RESOURCE_ENERGY)
             {
                const distance =  Game.map.getRoomLinearDistance(roomName,order.roomName);
                if(distance>10)
                {
                    continue;
                }
             }

             const fee = this.CalResourceTransactionFee(roomName,order.roomName,ResourceType);

             // 这里的价格可能需要处理一下,或者是房间在距离范围10格之内
             if(order.price > resourcePrice+fee ||  Game.map.getRoomLinearDistance(roomName,order.roomName,true)<20)
             {
                 // 消费+价格小于 平均价格就购买
                 const soldcount = Math.min(leftamnount,order.amount);
                 leftamnount-=soldcount;

                 Game.market.deal(order.id,soldcount,roomName);
                 if(leftamnount<=0)
                 {
                     return true;
                 }
             }
              //global.LogDebug(`出售资源: ${roomName}_${ResourceType}_购买价格:${resourcePrice} 路费:${fee}  订单价格:${order.price}`);
         }
         return false;
     }

    private UpdatePriceHistory()
    {
        const histtory = Game.market.getHistory();
        // const hisrecord:Record<string,PriceHistory> = {};
        // Memory.Debug[`更新历史记录${Game.time}`] = histtory;
        // console.log(`更新历史记录 ${JSON.stringify(histtory)}`);

        for(const item of histtory)
        {
            const his:IPriceHistory={time:Game.time,...item};
            if(this.MarketData.PriceHistory[item.resourceType] == undefined)
            {
                this.MarketData.PriceHistory[item.resourceType] = [];
            }
            this.MarketData.PriceHistory[item.resourceType].push(his);
            if( this.MarketData.PriceHistory[item.resourceType].length > 10)
            {
                this.MarketData.PriceHistory[item.resourceType].shift();
            }
        }
    }

    private UpdateEquation()
    {
        for(const resType in this.MarketData.PriceHistory)
        {
            const expr = this.Equation(resType as MarketResourceConstant,this.MarketData.PriceHistory[resType as MarketResourceConstant]);
            this.MarketData.PriceEquation[resType as MarketResourceConstant]=expr;
        }
    }
    private IsUpdate()
    {
        // console.log(`market tobe update ${this.MarketData.LastUpdateTime+this.TimeSpawn} <${Game.time} `);
        return this.MarketData.LastUpdateTime + this.TimeSpawn < Game.time;
    }


    private Equation(type:MarketResourceConstant,PriceHistory:IPriceHistory[]):{b:number,a:number}
    {

        const equation={b:0,a:0};
        // 求PriceHistory time的平均数
        const n = PriceHistory.length;
        const avg_x =  _.sum(PriceHistory,item=>item.time)/n;
        const avg_y =  _.sum(PriceHistory,item=>item.avgPrice)/n;

        let x2 = 0;
        let xy = 0;
        for(const item of PriceHistory)
        {
            xy += item.time * item.avgPrice;
            x2 += item.time * item.time;

        }

        equation.b = (xy - avg_x * avg_y) / (x2 - avg_x * avg_x);
        equation.a = avg_y - equation.b * avg_x;
        return equation;
    }

    private CalResourceTransactionFee(roomA:string,roomB:string,resourceType:MarketResourceConstant)
    {
        const cost = Game.market.calcTransactionCost(1000, roomA, roomB);
        const energyPrice = this.GetBasePrice(RESOURCE_ENERGY);
        return cost*energyPrice*1.0/1000;
    }

    private GetResourceTransactionFee(distance:number)
    {
        const cost = this.GetTransactionCost(1000,distance);
        const energyPrice = this.GetBasePrice(RESOURCE_ENERGY);
        return cost*energyPrice*1.0/1000;
    }
    private GetTransactionCost(amount:number,distance:number)
    {
        const cost =Math.ceil( amount * (Math.log(0.1*distance + 0.9) + 0.1) );
        return cost;
    }

    // private AddMyDeal(order:IMyDeal)
    // {
    //     this.MarketData.marketdeals[order.orderId]= order;
    // }
}
